﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using XMLGener.Utils;

namespace XMLGener.Data
{
  
    /// <summary>
    /// This class is used for data types definition.
    /// </summary>
    public class XDataType:XMLGenerNode
    {
        /// <summary>
        /// Constructor wihich data will be loaded
        /// from xml element.
        /// </summary>
        /// <param name="xmlElem"></param>
        public XDataType(XmlElement xmlElem)
            : base(null, xmlElem)
        {
            this.xtype = XNodeType.DATATYPE;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="xmlElem"></param>
        public XDataType( XMLGenerDataTypes parent,XmlElement xmlElem)
            : base(parent, xmlElem)
        {
            this.xtype = XNodeType.DATATYPE;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parent"></param>
        public XDataType(XMLGenerNode parent)
            : base(XMLGenerNode.XDATATYPE_STR, parent)
        {
            this.xtype = XNodeType.DATATYPE;
        }

        /// <summary>
        /// Indentification, it this data type belongs to 
        /// base or user-defined data types. Base types are 
        /// in settings.xml .
        /// </summary>
        public bool isBaseType = false;

        /// <summary>
        /// 
        /// </summary>
        protected override void defaultInitialization()
        {
            base.defaultInitialization();
            this.nodeTypeStr = XMLGenerNode.XDATATYPE_STR;
        }



        //---------------------------
        // attributes from xml
        //---------------------------

        /// <summary>
        /// Function will change value into valid name
        /// of data type. Function will also checks duplicities in all data types.
        /// </summary>
        /// <param name="value">name of element or attribute for validation</param>
        /// <returns>valid string</returns>
        protected override string validateName(string value)
        {
            string trim = base.validateName(value);
            // it is important  to get all types (included this instance) 
            // otherwise infinite recursion will happend
            XDataTypeCollection allTypes = XMLGenerDataTypes.getDefaultTypes();
            if (ownerDocument != null)
                allTypes = this.ownerDocument.getAllDataTypes();
            else
                allTypes = XMLGenerDataTypes.getDefaultTypes();
            int i = 1;
            string potentialName = trim;
            bool nameIsOk = false;
            while (!nameIsOk && allTypes!=null)
            {
                nameIsOk = true;
                foreach (XMLGenerNode node in allTypes)
                {
                    if (node != this && node.name == potentialName)
                    {
                        nameIsOk = false;
                        potentialName = trim + i.ToString();
                        i++;
                    }
                    if (nameIsOk && node == this)
                        break;
                }
            }

            return potentialName;
        }

        /// <summary>
        /// Return avialable types for this type of node
        /// </summary>
        /// <returns>list of strings</returns>
        public override XDataTypeCollection listAvailabeTypes()
        {
            return XMLGenerDataTypes.getDefaultTypes();

        }

        /// <summary>
        /// Return base data types as potential parents of this
        /// data type
        /// </summary>
        /// <returns>list of data types</returns>
        public override XDataTypeCollection listAvailabeParents()
        {
            return XMLGenerDataTypes.getDefaultTypes();
        }

        //------------------------
        // data type restrictions
        //------------------------

        private XDataType _parentDataType = null;
        
        /// <summary>
        /// Name of parent type of this data type. Default type is "any".
        /// </summary>
        public override string parentTypeName
        {
            get { return getXMLParam("parentType", "any"); }
            set 
            {
                base.parentTypeName = value;
                parentIsDefined = false;
                resetCachedProperties();
            }
        }
        private void resetCachedProperties()
        {
            _lengthNotChanged = false;
            _minLengthNotChanged = false;
            _maxLengthNotChanged = false;
            _minInclusiveNotChanged = false;
            _maxInclusiveNotChanged = false;
            _minExclusiveNotChanged = false;
            _maxExclusiveNotChanged = false;

            _useLengthNotChagned = false;
            _useMinLengthNotChanged = false;
            _useMaxLengthNotChanged = false;
            _useMinInclusiveNotChanged = false;
            _useMaxInclusiveNotChaned = false;
            _useMinExclusiveNotChanged = false;
            _useMaxExclusiveNotChanged = false;
            _useEnumerationNotChanged = false;
            _usePatternNotChanged = false;
            

        }

        private bool parentIsDefined = false;

        /// <summary>
        /// Parent data type property. If parent is "any", property is null.
        /// </summary>
        public XDataType parentDataType
        {
            get{
                if (!parentIsDefined)
                {
                    if (parentTypeName == "any")
                    {
                        this._parentDataType = null;
                    }else
                    if (this.ownerDocument != null)
                    {
                        this._parentDataType = this.ownerDocument.getAllDataTypes().getItem(this.parentTypeName) as XDataType;
                    }
                    else
                    {
                        this._parentDataType = XMLGenerDataTypes.getDefaultTypes().getItem(this.parentTypeName) as XDataType;
                    }
                    parentIsDefined = true;
                }
                return this._parentDataType;
            }
        }


        /// <summary>
        /// this property is used as lock for setting linked restrictions
        /// eg. min/maxInclusive
        /// </summary>
        private string _checkLockId = null;

        private string checkLockId
        { 
            get{ return _checkLockId;}
            set{
                if (_checkLockId == null || value == null)
                    _checkLockId = value;
            }
        }
        
        private void checkRestrictions()
        {
            checkRestrictions(null);
        }
        private void checkRestrictions( string lockId)
        {
            if (checkLockId == lockId)
                checkLockId = null;
            else
                return;
            //return;
            string sourceName = "data type: \"" + this.name + "\"";
            if (useEnumeration && (usePattern || useLength || useMinLength || useMaxLength ||
                    useMinInclusive || useMaxInclusive ||useMinExclusive || useMaxExclusive))
            {
                XLogger.add(sourceName,
                    "Enumeration can be in conflict with other restrictions. In data generating can be one of rectriction ignored.",
                    XLogMessage.LogType.WARNING);
                XLogger.add(sourceName,
                    "For proper generating select only Enumeration restriction.",
                    XLogMessage.LogType.ERROR_HINT);
                return;
            }

            if (usePattern)
            {
                if (useLength || useMinLength || useMaxLength ||
                    useMinInclusive || useMaxInclusive ||
                    useMinExclusive || useMaxExclusive)
                {
                    XLogger.add(sourceName,
                        "Pattern can be in conflict with other restrictions. In data generating can be one of rectriction ignored.",
                        XLogMessage.LogType.WARNING);
                    XLogger.add(sourceName,
                        "For proper generating select only pattern restriction.",
                        XLogMessage.LogType.ERROR_HINT);
                    return;
                }
            }
          
            if (useLength)
            {
                if (useMinLength && (minLength > length))
                {
                    XLogger.add(sourceName,
                        "Restriction Min Length is greather than Length.",
                        XLogMessage.LogType.ERROR);
                    return;
                }
                if (useMinLength && (maxLength < length))
                {
                    XLogger.add(sourceName,
                        "Restriction Max Length is less than Length.",
                        XLogMessage.LogType.ERROR);
                    return;
                }
            }

            if (useMinLength && useMaxLength && (minLength > maxLength))
            {
                XLogger.add(sourceName,
                        "Restriction Max Length is less than Min Length.",
                        XLogMessage.LogType.ERROR);
                    return;
            
            }


            if (useMaxExclusive || useMaxInclusive || useMinExclusive || useMinInclusive)
            {
                if (useLength || useMinLength || useMaxLength)
                {
                    XLogger.add(sourceName,
                        "Restriction (Min/Max) Length and using Min/Max Inclusive/Exclusive may be in conflict. One of these rectriction may be ignored in generating.",
                        XLogMessage.LogType.WARNING);
                    return;
                }
            }



        }

        private static decimal MAX_DECIMAL =  decimal.MaxValue;
        private static decimal MIN_DECIMAL =  decimal.MinValue;

        //--------------------
        // length

        private decimal _length ;
        private bool _lengthNotChanged = false;
        /// <summary>
        /// Length restriction of data type
        /// </summary>
        public virtual decimal length
        {
            get {
                if (_lengthNotChanged) return _length;
                _lengthNotChanged = true;
                return _length= getDecimalXMLParam("length", (useLengthInherited) ? lengthInherited : minLengthInherited, (useLengthInherited) ? lengthInherited : maxLengthInherited);
            }
            set {
                if (isBaseType) return;
                setDecimalXMLParam("length", value); 
                _lengthNotChanged = (_length == value);
                checkRestrictions();
            }
        }

        private bool _useLength = false;
        private bool _useLengthNotChagned = false;
        /// <summary>
        /// Indication, if length retriction must be accepted
        /// </summary>
        public virtual bool useLength
        {
            get {
                if (_useLengthNotChagned) return _useLength;
                _useLengthNotChagned = true;
                return _useLength=getBoolParam("uselength", (isBaseType) ? useLengthInherited : false); }
            set {
                if (isBaseType) return;
                setBoolParam("uselength", value); 
                _useLengthNotChagned = (_useLength == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public decimal lengthInherited
        {
            get { return (parentDataType != null && useLengthInherited) ? parentDataType.length : MAX_DECIMAL; }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useLengthInherited
        {
            get { return (parentDataType != null )? parentDataType.useLength:false; }
        }

        
        //--------------------
        // minlength 

        private decimal _minLength;
        private bool _minLengthNotChanged = false;
        /// <summary>
        /// Minimal length restriction of data type
        /// </summary>
        public virtual decimal minLength
        {
            get {
                if (_minLengthNotChanged) return _minLength;
                _minLengthNotChanged = true;
                return _minLength= getDecimalXMLParam("minlength", minLengthInherited, maxLengthInherited); }
            set {
                if (isBaseType) return;
                decimal val = value;
                checkLockId = "minlength";
                if (val < 0) val = 0;
                setDecimalXMLParam("minlength", val);
                if (val > maxLength) maxLength = val;
                _minLengthNotChanged = (_minLength == value);
                checkRestrictions("minlength");
            }
        }

        private bool _useMinLength;
        private bool _useMinLengthNotChanged = false;
        /// <summary>
        /// Indication, if minimal length retriction must be accepted
        /// </summary>
        public virtual bool useMinLength
        {
            get {
                if (_useMinLengthNotChanged) return _useMinLength;
                _useMinLengthNotChanged = true;
                return _useMinLength=getBoolParam("useminlength", useMinLengthInherited);
            }
            set {
                if (isBaseType) return; 
                setBoolParam("useminlength", value); 
                _useMinLengthNotChanged = (_useMinLength == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public decimal minLengthInherited
        {
            get { return (parentDataType != null && useMinLengthInherited) ? parentDataType.minLength : 0; }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useMinLengthInherited
        {
            get { return (parentDataType != null) ? parentDataType.useMinLength : false; }
        }


        //--------------------
        // max length
        private decimal _maxLength;
        private bool _maxLengthNotChanged = false;
        /// <summary>
        /// Maximal length restriction of data type
        /// </summary>
        public virtual decimal maxLength
        {
            get {
                if (_maxLengthNotChanged) return _maxLength;
                _maxLengthNotChanged = true;
                return _maxLength= getDecimalXMLParam("maxlength", minLengthInherited, maxLengthInherited);
            }
            set {
                if (isBaseType) return;
                decimal val = value;
                checkLockId = "maxlength";
                if (val < 0) val = 0;
                setDecimalXMLParam("maxlength", val);
                if (val < minLength) minLength = val;
                _maxLengthNotChanged = (_maxLength == value);
                checkRestrictions("maxlength");
            }
        }

        private bool _useMaxLength;
        private bool _useMaxLengthNotChanged = false;
        /// <summary>
        /// Indication, if maximal length retriction must be accepted
        /// </summary>
        public virtual bool useMaxLength
        {
            get {
                if (_useMaxLengthNotChanged) return _useMaxLength;
                _useMaxLengthNotChanged = true;
                return _useMaxLength= getBoolParam("usemaxlength", useMaxLengthInherited); }
            set {
                if (isBaseType) return; 
                setBoolParam("usemaxlength", value); 
                _useMaxLengthNotChanged = (_useMaxLength == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public decimal maxLengthInherited
        {
            get { return (parentDataType != null && useMaxLengthInherited) ? parentDataType.maxLength : MAX_DECIMAL; }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useMaxLengthInherited
        {
            get { return (parentDataType != null) ? parentDataType.useMaxLength : false; }
        }

        //--------------------
        // minInclusive 

        private decimal _minInclusive;
        private bool _minInclusiveNotChanged = false;
        /// <summary>
        /// Minimal inclusion restriction of data type
        /// </summary>
        public virtual decimal minInclusive
        {
            get {
                if (_minInclusiveNotChanged) return _minInclusive;
                _minInclusiveNotChanged = true;
                return _minInclusive=getDecimalXMLParam("mininclusive", minInclusiveInherited, maxInclusiveInherited); }
            set {
                if (isBaseType) return;
                setDecimalXMLParam("mininclusive", value);
                if (value > maxInclusive) maxInclusive = value;
                _minInclusiveNotChanged = (_minInclusive == value);
            }
        }

        private bool _useMinInclusive;
        private bool _useMinInclusiveNotChanged = false;
        /// <summary>
        /// Indication, if minimal inclusion retriction must be accepted
        /// </summary>
        public virtual bool useMinInclusive
        {
            get {
                if (_useMinInclusiveNotChanged) return _useMinInclusive;
                _useMinInclusiveNotChanged = true;
                return _useMinInclusive=getBoolParam("usemininclusive", useMinInclusiveInherited); 
            }
            set {
                if (isBaseType) return; 
                setBoolParam("usemininclusive", value); 
                _useMinInclusiveNotChanged = (_useMinInclusive == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public decimal minInclusiveInherited
        {
            get { return (parentDataType != null && useMinInclusiveInherited) ? parentDataType.minInclusive : MIN_DECIMAL; }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useMinInclusiveInherited
        {
            get { return (parentDataType != null) ? parentDataType.useMinInclusive : false; }
        }


        //--------------------
        // maxInclusive 

        private decimal _maxInclusive;
        private bool _maxInclusiveNotChanged = false;
        /// <summary>
        /// Maximal inclusion restriction of data type
        /// </summary>
        public virtual decimal maxInclusive
        {
            get {
                if (_maxInclusiveNotChanged) return _maxInclusive;
                _maxInclusiveNotChanged = true;
                return _maxInclusive=getDecimalXMLParam("maxinclusive", minInclusiveInherited, maxInclusiveInherited);
            }
            set {
                if (isBaseType) return;
                setDecimalXMLParam("maxinclusive", value);
                if (value < minInclusive) minInclusive = value;
                _maxInclusiveNotChanged = (_maxInclusive==value);
            }
        }

        private bool _useMaxInclusive;
        private bool _useMaxInclusiveNotChaned = false;
        /// <summary>
        /// Indication, if maximal inclusion retriction must be accepted
        /// </summary>
        public virtual bool useMaxInclusive
        {
            get {
                if (_useMaxInclusiveNotChaned) return _useMaxInclusive;
                _useMaxInclusiveNotChaned=true;
                return _useMaxInclusive=getBoolParam("usemaxinclusive", useMaxInclusiveInherited); }
            set {
                if (isBaseType) return; 
                setBoolParam("usemaxinclusive", value); 
                _useMaxInclusiveNotChaned = (_useMaxInclusive == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public decimal maxInclusiveInherited
        {
            get { return (parentDataType != null && useMaxInclusiveInherited) ? parentDataType.maxInclusive : MAX_DECIMAL; }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useMaxInclusiveInherited
        {
            get { return (parentDataType != null) ? parentDataType.useMaxInclusive : false; }
        }

        //--------------------
        // minExclusive 

        private decimal _minExclusive;
        private bool _minExclusiveNotChanged = false;
        /// <summary>
        /// Minimal exclusion restriction of data type
        /// </summary>
        public virtual decimal minExclusive
        {
            get {
                if (_minExclusiveNotChanged) return _minExclusive;
                _minExclusiveNotChanged = true;
                return _minExclusive=getDecimalXMLParam("minexclusive", minExclusiveInherited, maxExclusiveInherited);
            }
            set {
                if (isBaseType) return;
                setDecimalXMLParam("minexclusive", value);
                if (value > maxExclusive) maxExclusive = value;
                _minExclusiveNotChanged = (_minExclusive == value);
            }
        }

        private bool _useMinExclusive;
        private bool _useMinExclusiveNotChanged = false;
        /// <summary>
        /// Indication, if minimal exclusion retriction must be accepted
        /// </summary>
        public virtual bool useMinExclusive
        {
            get {
                if (_useMinExclusiveNotChanged) return _useMinExclusive;
                _useMinExclusiveNotChanged = true;
                return _useMinExclusive=getBoolParam("useminexclusive", useMinExclusiveInherited);
            }
            set {
                if (isBaseType) return; 
                setBoolParam("useminexclusive", value); 
                _useMinExclusiveNotChanged = (_useMinExclusive == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public decimal minExclusiveInherited
        {
            get { return (parentDataType != null && useMinExclusiveInherited) ? parentDataType.minExclusive : MIN_DECIMAL; }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useMinExclusiveInherited
        {
            get { return (parentDataType != null) ? parentDataType.useMinExclusive : false; }
        }

        //--------------------
        // maxExclusive 

        private decimal _maxExclusive;
        private bool _maxExclusiveNotChanged = false;
        /// <summary>
        /// Maximal exclusion restriction of data type
        /// </summary>
        public virtual decimal maxExclusive
        {
            get {
                if (_maxExclusiveNotChanged) return _maxExclusive;
                _maxExclusiveNotChanged = true;
                return _maxExclusive=getDecimalXMLParam("maxexclusive", minExclusiveInherited, maxExclusiveInherited); 
            }
            set {
                if (isBaseType) return;
                setDecimalXMLParam("maxexclusive", value);
                if (value < minExclusive) minExclusive = value;
                _maxExclusiveNotChanged = (_maxExclusive == value);
            }
        }

        private bool _useMaxExclusive;
        private bool _useMaxExclusiveNotChanged = false;
        /// <summary>
        /// Indication, if maximal exclusion retriction must be accepted
        /// </summary>
        public virtual bool useMaxExclusive
        {
            get {
                if (_useMaxExclusiveNotChanged) return _useMaxExclusive;
                _useMaxExclusiveNotChanged = true;
                return _useMaxExclusive=getBoolParam("usemaxexclusive", useMaxExclusiveInherited);
            }
            set {
                if (isBaseType) return; 
                setBoolParam("usemaxexclusive", value); 
                _useMaxExclusiveNotChanged = (_useMaxExclusive == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public decimal maxExclusiveInherited
        {
            get { return (parentDataType != null && useMaxExclusiveInherited) ? parentDataType.maxExclusive : MAX_DECIMAL; }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useMaxExclusiveInherited
        {
            get { return (parentDataType != null) ? parentDataType.useMaxExclusive : false; }
        }

        //--------------------
        // pattern 

        /// <summary>
        /// Pattern restriction of data type
        /// </summary>
        public virtual string pattern
        {
            get { return getXMLParam("pattern", ""); }
            set {
                if (isBaseType) return;
                setXMLParam("pattern", value); 
            }
        }

        private bool _usePattern;
        private bool _usePatternNotChanged = false;
        /// <summary>
        /// Indication, if pattern retriction must be accepted
        /// </summary>
        public virtual bool usePattern
        {
            get {
                if (_usePatternNotChanged) return _usePattern;
                _usePatternNotChanged = true;
                return _usePattern=getBoolParam("usepattern", usePatternInherited);
            }
            set {
                if (isBaseType) return;
                setBoolParam("usepattern", value); 
                _usePatternNotChanged = (_usePattern == value);
                checkRestrictions();
            }
        }
        
        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool usePatternInherited
        {
            get { return (parentDataType != null) ? parentDataType.usePattern : false; }
        }

        //--------------------
        // enumeration 

        /// <summary>
        /// Enumeration restriction of data type
        /// </summary>
        public virtual string enumeration
        {
            get { return getEnumeration(); }
            set { saveEnumeration(value); }
        }

        private bool _useEnumeration;
        private bool _useEnumerationNotChanged = false;
        /// <summary>
        /// Indication, if enumeration retriction must be accepted
        /// </summary>
        public virtual bool useEnumeration
        {
            get {
                if (_useEnumerationNotChanged) return _useEnumeration;
                _useEnumerationNotChanged = true;
                return _useEnumeration=getBoolParam("useenumeration", useEnumerationInherited);
            }
            set { 
                setBoolParam("useenumeration", value); 
                _useEnumerationNotChanged = (_useEnumeration == value);
                checkRestrictions();
            }
        }

        /// <summary>
        /// Parent restriction
        /// </summary>
        public virtual bool useEnumerationInherited
        {
            get { return (parentDataType != null) ? parentDataType.useEnumeration : false; }
        }

        /// <summary>
        /// New line sepearators
        /// </summary>
        private static Char[] separators = new Char[] { Environment.NewLine.ToCharArray()[0],'\r','\n'};
        
        /// <summary>
        /// Save enumerations into xml, where value contains enumerations separated by new line.
        /// Each enumeration will be saved in element "enumeration" as child element of "enumerations"
        /// </summary>
        /// <param name="value"></param>
        private void saveEnumeration(string value)
        {
            
            XmlElement enumElem = XMLUtils.getOrCreateSubElement(this.xmlElem, "enumerations");
            enumElem.RemoveAll();
            if (string.IsNullOrEmpty(value))
                return;
            string[]splited= value.Split(separators,StringSplitOptions.RemoveEmptyEntries);
            if (splited == null || splited.Length < 1)
                return;
            foreach (string str in splited)
            {
                XmlElement newEnum = XMLUtils.createSubElement(enumElem, "enumeration");
                if (newEnum != null && !string.IsNullOrEmpty(str))
                {
                    newEnum.InnerText = str.Trim();
                }
            }
        }

        /// <summary>
        /// Return list of enumeration from xml definition 
        /// where each enumeration is element "enumeration"
        /// as result, where each enumeration is separated by new line.
        /// </summary>
        /// <returns></returns>
        private string getEnumeration()
        {
            XmlElement enumElem = XMLUtils.getOrCreateSubElement(this.xmlElem, "enumerations");
            StringBuilder sb = new StringBuilder();
            XmlElement childEnum = XMLUtils.getSubElement(enumElem, "enumeration");
            string enumStr = "";
            while (childEnum != null)
            {
                enumStr = childEnum.InnerText;
                if (enumStr != null) enumStr = enumStr.Trim();
                if (!string.IsNullOrEmpty(enumStr))
                {
                    if (sb.Length > 1) sb.Append(Environment.NewLine);
                    sb.Append(enumStr);
                }
                childEnum = XMLUtils.getNextElement(childEnum, "enumeration");
            }
            return sb.ToString();
        }
        public List<string> getEnumerationInList()
        {
            List<string> result = new List<string>();
            XmlElement enumElem = XMLUtils.getOrCreateSubElement(this.xmlElem, "enumerations");
            XmlElement childEnum = XMLUtils.getSubElement(enumElem, "enumeration");
            string enumStr = "";
            while (childEnum != null)
            {
                enumStr = childEnum.InnerText;
                if (enumStr != null) enumStr = enumStr.Trim();
                if (!string.IsNullOrEmpty(enumStr))
                {
                    result.Add(enumStr);
                }
                childEnum = XMLUtils.getNextElement(childEnum, "enumeration");
            }
            return result;
        }

        /// <summary>
        /// This method prepares this data restriction for module settings.
        /// </summary>
        /// <returns></returns>
        public string getRestrictionString()
        {
            validateData();
            XmlElement restrictionXML = this.xmlElem.OwnerDocument.CreateElement("restriction");
            XmlElement copy = this.xmlElem.CloneNode(true) as XmlElement;
            foreach (XmlNode childNode in copy.ChildNodes)
                restrictionXML.AppendChild(childNode);
            foreach (XmlAttribute attr in copy.Attributes)
                restrictionXML.SetAttribute(attr.Name, attr.Value);

            return restrictionXML.OuterXml;
        }

        public void validateData()
        {
            checkLockId = "valdatingData";
            length = length;
            minLength = minLength;
            maxLength = maxLength;
            minInclusive = minInclusive;
            maxInclusive = maxInclusive;
            minExclusive = minExclusive;
            maxExclusive = maxExclusive;
            pattern = pattern;
            enumeration = enumeration;
            useLength = useLength;
            useMinLength = useMinLength;
            useMaxLength = useMaxLength;
            useMinInclusive = useMinInclusive;
            useMaxExclusive = useMaxExclusive;
            usePattern = usePattern;
            useEnumeration = useEnumeration;
            checkRestrictions(checkLockId);

        }
        public bool modulesNeedRefresh = true;

        /// <summary>
        /// Function sets attribute's decimal content.
        /// </summary>
        /// <param name="name">attribute's name</param>
        /// <param name="value">conent</param>
        protected override void setDecimalXMLParam(string name, decimal value)
        {
            modulesNeedRefresh = true;
            base.setDecimalXMLParam(name, value);
        }

        /// <summary>
        /// Function sets attribute's boolean value into xml
        /// </summary>
        /// <param name="name">attribute's name</param>
        /// <param name="value">value for set</param>
        protected virtual void setBoolParam(string name, bool value)
        {
            modulesNeedRefresh = true;
            base.setBoolParam(name, value);
        }

        private List<XModuleDefinitionInstance> _moduleList = new List<XModuleDefinitionInstance>();
        private List<XModuleDefinitionInstance> _matchingModules = new List<XModuleDefinitionInstance>();
        public void addMatchingModule(XModuleDefinitionInstance mod)
        {
            if (this._matchingModules.Contains(mod))
                return;
            this._matchingModules.Add(mod);
            if (this.parentDataType != null)
                this.parentDataType.addMatchingModule(mod);
            else
                anyDataType.addMatchingModule(mod);
                
        }
        public List<XModuleDefinitionInstance> modules
        {
            get {
                if (modulesNeedRefresh)
                {
                    loadModules();
                }
                return _moduleList;
            }
        }
        public void loadModules()
        {
            if (!modulesNeedRefresh)
                return;
            modulesNeedRefresh = false;
            this._moduleList.Clear();
            this._moduleList.AddRange(this._matchingModules);
            if (this.name == "any" || string.IsNullOrEmpty(this.parentTypeName) || isBaseType)
            {
                return;
            }
            foreach (XModuleDefinitionInstance specificModule in XModuleManager.modDefinitionList.Values)
            {
                if (specificModule.restirction.dataType == this.name)
                    _moduleList.Add(specificModule);
            }

            XDataType parent= (this.parentDataType==null)?anyDataType:this.parentDataType;
            if (parent==null || (parent.name == "any" && isBaseType))
                return;
            foreach (XModuleDefinitionInstance mod in parent.modules)
            {
                XModuleRestrictions restr = mod.restirction;
                if ((!restr.length && this.useLength) ||
                    (!restr.minLength && this.useMinLength) ||
                    (!restr.maxLength && this.useMaxLength) ||
                    (!restr.minInclusive && this.useMinInclusive) ||
                    (!restr.maxInclusive && this.useMaxInclusive) ||
                    (!restr.minExclusive && this.useMinExclusive) ||
                    (!restr.maxExclusibe && this.useMaxExclusive) ||
                    (!restr.pattern      &&  this.usePattern) ||
                    (!restr.enumeration && this.useEnumeration))
                        continue;
                if (!_moduleList.Contains(mod))
                    _moduleList.Add(mod);
            }
        }

        private static XDataType _anyDataType=null;

        private static XDataType anyDataType {
            get
            {
                if (_anyDataType == null)
                {
                    XDataTypeCollection col = XMLGenerDataTypes.getDefaultTypes();
                    if (col != null)
                        _anyDataType = col.getItem("any") as XDataType;

                }
                return _anyDataType;
            }
        }


       


        
    }
}
